home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Apple Script / OSAX / pgmTool / about programmer tool osax next >
Text File  |  1993-07-08  |  17KB  |  199 lines

  1. The following is description of some osax that may be useful for programmer, bear in mind this is not for the average end user.
  2.  
  3. One of the strongest point of Apple Events and AppleScript is the richness of data type. Every type of data that can appear on the clipboard and resource fork can be an Apple Events/AppleScript data type. The very first specification  of Apple Event is not like this. Rather you have 0 is integer, 1 is real, 2 is text so on for a total of 10 data types and everything else is binary data. I think OLE2 eventually took this approach. I think my most important contribution to Apple Events is the introduction of the use of four letter words in data type in Apple Events. Of course it does not require a lot of brain to do this since this is also in the rest of the Macintosh toolbox. However since data types are so general, they are treated as black box and it is up to the application to generate and interpret them. For example, the date value in AppleScript can be converted to text, but that is about all that you can do in a script in this version of AppleScript. This may be changed in the next version of AppleScript. In general we want to generate and interpret general data type without waiting for a new version of AppleScript or application.
  4.  
  5. For example, if we examine the date value, it is not difficult to guess that it is a 64 bit number indicating the number of seconds from 1904. So if we have a type casting osax that returns the same data but with a new data type name, then we can interpret the data as a number.
  6.  
  7. Cast (current date) to «class comp»
  8.  
  9. Then you can do calucation with a date, like telling how many seconds apart two date values are.
  10.  
  11. If you are trying to interpet a more complicated data structure, simple casting like this is not going to help you. One way to get around the problem is to cast a portion of the data at a time. Then you can take part a data structure and read it in many calls. You can also do the reverse and assembling a data type. This is the trick used in the AEXCMD stack. In that stack there are XCMD to take apart and build up descriptors. In fact you can write coercion handler in HyperTalk using these technique.
  12.  
  13. Cast x from 5 length 4 to integer.
  14.  
  15. This let you read bytes 5 through 8 of a variable as a integer.
  16.  
  17. However, it would take you many calls to take apart even a simple data strucutre like SIZE. Ideally we need to do it in a single operation. What we need is the equivalent of DeRez. In the SDK goodies folder, there is an Show variables OSAX that let you look at a variable. One option allows to look at the variable using a ResEdit template. However that is done interactively so it cannot be used for data manipulation in a script. If if take the code out and put into the type casting osax, then we can do the equivalent of DeRez.
  18.  
  19. Cast x using template "STR#"
  20.  
  21. or if x is already of the type "STR#" we can say
  22.  
  23. Cast x
  24.  
  25. What we generate is a list, for example {"alpha","beta"}, with other data structure it may be a record. We still have two problems, how can we construct the variable from simple data, and what if we don't have a predefined template? To solve the first problem, we need the equivalent of Rez. We can tak a list or a record and cast it to a data structure using a template type.
  26.  
  27. Cast {"alpha","beta"} to «class abcd» using template "STR#"
  28.  
  29. or if we the template type is same as the type to be casted
  30.  
  31. Cast {"alpha","beta"} to "STR#"
  32.  
  33. So we can generate any data structure. Now back to the problem of a template that is not predefined. It turns out while the template parameter is usally a class name and the name is used to get a TMPL resource, the template parameter can also be direct TMPL data. How can we generate a TMPL? Like any other data with a predefined template a TMPL data can be generated by casting, for example this is a TMPL that treats a 32 bits integer as two 16 bits integer
  34.  
  35. cast {{label:"high_word", type:«class DWRD»}, {label:"low_word", type:«class DWRD»}} to «class TMPL»
  36.  
  37. or to generate a template that treats a long integer as 32 bits boolean
  38.  
  39. set x to {}
  40. repeat with i from 1 to 32
  41.     set x to x & {{"bit_" & (32 - i), "BBIT"}}
  42. end repeat
  43. cast x to "TMPL"
  44.  
  45. Many people think that aete is very complicated, so it provides a good test case. To reduce the size of the output let us return it as a list rather than a record.
  46.  
  47. tell application "Clipboard Magician"
  48.     set x to (Read Resources From File) -- pick a resource from a file
  49. end tell
  50. cast x without label
  51.  
  52. We pick the aete from the Numeric osax and get as a result
  53.  
  54. {0, 144, 0, 0, {{"System Object Suite", "
  55. Copyright ® 1993 Apple Computer, Inc. All rights reserved.", «class syso», 1, 1, {{"round", "Round number to integer.", «class syso», «class rond», integer, "rounded value", false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, «class exte», "number to round", true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, {{"rounding", «class dire», «class olie», "specify rounding direction, default is rounding to nearest", true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}}}, {"random number", "Generate a random number.", «class syso», «class rand», «class ****», "random number returned", false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, «class ****», " number to randomize (If included, random number from 0 to this value. If no number specified, then a value between 0 and 1 is returned.)", true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, {{"from", «class from», «class ****», "from value", true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {"to", «class to  », «class ****», "to value", true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {"with seed", «class seed», «class ****», "", true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}}}}, {}, {}, {{«class olie», {{"up", «class rndU», "round up"}, {"down", «class rndD», "round down"}, {"toward zero", «class rndZ», "round toward zero"}, {"to nearest", «class rndN», "round to nearest"}}}}}}}
  56.  
  57. So it works, and you can cast this back into an aete. We could have pick a bigger aete, but that would overflow the result window of the script editor.
  58.  
  59. You may say, this works but is this easier to read than DeRez output? It certainly is not. If we had included the labels, It would be a little bit better but in general a complex list is difficult to read. However, being a AppleScript list means that it is easy to manipulate. For example, using the ListToText command with delimiter "hierarchical" in the XCMD osax would dipslay the list in a DeRez like format. The Show Variables osax would let you look inside the list interactively. I am quite sure there are many way to display the list in a more intelligent manner, and this is an area that we need to investigate more.
  60.  
  61. Now we can generate any reasonable data type, we need to operate on it. We can make trap call if we have a inline osax that let you specify and execute machine code. We have include a very crude and experimental osax. First some warning first. This is not fully designed yet and hardly been tested, so use with extreme caution. Even if it is fully functional and debugged, an inline osax is dangerous. One mistake in your code and it will crash. And if you have guest logon for program linking you are opening your machine for abuse from other people. So think twice before you install the inline osax.
  62.  
  63. However, it can be a very powerful osax. For example you can get the gestalt by calling the toolbox directly
  64.  
  65. on Gestalt of selector
  66.     if (class of selector = string) and (length of selector = 4) then
  67.         return inline "202F,0004,A1AD,225F,2288,588F,3E80" with parameter {selector:{input, selector}, response:{output, integer}} returning OSErr
  68.     else
  69.         error "illegal selector"
  70.     end if
  71. end Gestalt
  72.  
  73. Gestalt of "evnt"
  74. Gestalt of "fpu "
  75.  
  76. ****************************************************************
  77.  
  78. We have put both the Show Variables osax and the Type Cast osax in the same file because they share the use of the template resources.
  79.  
  80. The syntax of the Type Cast osax as seen from the script editor is
  81.  
  82. cast: cast the variable in the direct parameter.
  83.     cast  anything  -- the variable
  84.         [from  integer]  -- starting character position, default is 1
  85.         [length  integer]  -- data length, default is to end
  86.         [to  type class]  -- rename variable as this type
  87.         [using template  anything]  -- template type name or template data
  88.         [label  boolean]  -- whether return a list or user record
  89.     Result:   anything
  90.  
  91. In the most simple form, the command is
  92.  
  93. cast x to "yyyy"
  94.  
  95. If x is not a list or a record, then it returns a value with the same value as x but the type is "yyyy". The result will be the same length as the source. This means that you may generate some illegal data, but that is always the risk when you are doing a cast in any language. An exception is integer. When something is cast into integer, the result is always 4 bytes. This means that if the source is less than 4 bytes, zeros will be added to the front. If the source is more than 4 bytes, only the first 4 bytes will be used. However the fact AppleScript integer is of the range ???? to ???? creates some complication. You can cast something into a integer and get back a 8 byte floating point number. This is something you have to watch out for.
  96.  
  97. Note that you can do
  98.  
  99. cast "A" to integer -- returns 65
  100. cast 65 to string -- returns "A"
  101.  
  102. they are the equivalent to the string commands
  103. ASCII number of "A"
  104. ASCII character of 65
  105.  
  106. the casting version is better because it works with multibyte character set.
  107.  
  108. The casting need not work on the whole variable. It can work on a subrange using the from and the length parameter. The "from" parameter is the starting position, the default is 1 and that means from the beginning. The "from" parameter can also be a negative number and that means count from the end. So -1 means the last character and -5 means the 5th character from the last. Length is the number of bytes in the casting range. The default is from the starting position to the end of the variable. Length can also be negative in which case you count to the left.
  109.  
  110. cast "abcdefg" to string from 2 -- all character except first => "bcdefg"
  111. cast "abcdefg" length 3 to string -- first 3 characters => "abc"
  112. cast "abcdefg" to string from 2 length 3 -- character 2 to 4 => "bcd"
  113. cast "abcdefg" to string from -2 -- last 2 character  => "fg"
  114. cast "abcdefg" from -2 length -3 to string -- last 3 character before the last one => "def"
  115. cast "abcdefg" from -1 to integer -- cast last character to integer => 103
  116.  
  117. If you do not specify the "to" parameter, then cast use a template to interpret the data. If the template parameter is either a class name or a string, it is treated as the four character name used to locate the template resource. If the class of the template parameter is «class TMPL», then the template data is used directly. Depending on the template, the cast result can either be a list or a record. For example, «class STR#» is a list
  118.  
  119. {"alpha","beta"}
  120.  
  121. while «class vers» is a record
  122.  
  123. {version_number:1, revision_number:0, revision_stage:128, build_number:0, language_integer:0, abbreviated_string:"1.0", get_info_string:"Apple's Scripting System v1.0"}
  124.  
  125. and «class TMPL» is a list of record
  126.  
  127. {{label:"The String", type:«class PSTR»}, {label:"Data", type:«class HEXD»}}
  128.  
  129. The label is automatically converted to lower case and space is converted to underline to avoid the | being used in the label. It takes time to generate the label and the result takes up more memory. So the "label" parameter can be used to omit the label. The default is true (include the label). Without the label, the previous examples looks like
  130.  
  131. {1, 0, 128, 0, 0, "1.0", "Apple's Scripting System v1.0"}
  132. {{"The String", «class PSTR»}, {"Data", «class HEXD»}}
  133.  
  134. If you do not specify the "to" parameter and the "template" parameter, the the class of the variable is used as the template name, i.e. cast x is equivalent to cast x using template (class of x).
  135.  
  136. If the tempate cannot be found, a hexdump of the data is returned.
  137.  
  138. It should be pointed out the result of a large variable would easily exceed the display limit of the script editor, especially when labels are included. So you may need some mean to export the data and view it here.
  139.  
  140. If the direct parameter is a list or a record, then casting take on a different meaning. It will be the equivalent of "Rez". The "from", "length", "label" parameter has no meaning and will be ignored in this version and returned as a parameter not understood in later versions.
  141.  
  142. The syntax is then
  143.  
  144. Cast aList to «class xxxx» using template yyyy
  145. If the template is template data, then the "to" parameter must be present. If yyyy is a template name, then the "to" parameter can be omitted.
  146.  
  147. Cast aList using template «class yyyy»  => Cast aList to «class yyyy» using template «class yyyy»
  148.  
  149. Cast aList to «class yyyy»  => Cast aList to «class yyyy» using template «class yyyy»
  150.  
  151. It is better not to use the last syntax since it may change in future.
  152.  
  153. The direct parameter can either be a list or a record. However in this version labels are just ignored and the record is treated as a list. This means that the data must be entered in the right order and you cannot depend on the label. Another thing to watch out for is that templates are usually not designed for used for this purpose. So some labels, in particular the label "reserved", are used repeatedly in the same record. I try to edit some of them out in the template in the osax, but I may miss some and user may be adding new template. AppleScript will collapse multiple fields with the same label into one field, so you have to watch out for it. Also it is often necessary to give a label to a list, but the label of LSTC,LSTB is usually *****. If you want a real label, you need to change it. Again I have edit some but not all the templates.
  154.  
  155. In the template definitation, data type names are different from Apple Events/AppleScript. For example, "long" in Apple Event is "DLNG" in ResEdit. When you are defining a template, within the template you should follow ResEdit naming convention. Although we do allow integer ("long") as a symonym to "DLNG", and character ("cha ") as a symonym to "CHAR". There can be more mapping in future. Both "DLNG" and "HLNG" are read out as integer, i.e. HLNG (also HWRD and HBYT) is not read as a hexadecimal string. The reason is that while for human reading HLNG as hexadecimal is better, when it is used in a script a number is more useful.
  156.  
  157. It should be noted this is a prototype version, and it is particular weak in error reporting. In you have mistake in your data to be "rezzed", the error is reported as paramErr, and it carries no information where the error occurs. This will be fixed in a future version after I figure out a way to point to the offending item. There may be misintreprtation of some of the data types in ResEdit template. Also performance can be improved in a future version.
  158.  
  159. Open issues
  160.  
  161. Casting from type with unknown template now returns hex string, should we return an error instead?
  162.  
  163. TNAM data is returned as typeType and show up in AppleScript as class name, and usually it ends up as «class xxxx», is it better to return as return it as four character string?
  164.  
  165. Should Boolean in AppleScript map to "BOOL", "BBYT" or "BBIT" in ResEdit?
  166.  
  167. Should we define yet another optional parameter so that HLNG, HWRD and HBYT would return hexadecimal string?
  168.  
  169. Should we keep the current meaning of "Cast aRecord to «class yyyy»", or should it mean "coerace aRecord to «class yyyy»" since AppleScript does not support "aRecord as «class yyyy»?
  170.  
  171. The terminology of "from" parameter is unrelated to the "to" parameter, so it can be confusing. Does anyone has a better suggestion?
  172.  
  173. The labels are converted to lower case with the script manager call transliterate. So it should work with other script system such as Kanji. But there may be problem if there are mulitple scripts. Should we have yet another parameter to leave the label unchanged?
  174.  
  175. For labels of list, since the labels are derived from the label of LSTC/LSTB/LSTZ, the label is usually just '*****', should we automatically generate name for them?
  176.  
  177.  
  178. About the Show Variables osax.
  179.  
  180. There has been very little change in the Show Variables osax, except that user record is now recognized and displayed as such. In fact, a lot of the template code has now been moved to the type cast osax so the tempate stuff is redunant. The template will be taken out in future and the osax will then concenterate its role on the smarter way of display of list and other data type.
  181.  
  182. Now that we recognize user record in Show variables, we may be tempted to do
  183.  
  184. Show variables {x:x, y:y, z,z}
  185.  
  186. instead of
  187.  
  188. Show variables {"x", x, "y", y, "z", z}
  189.  
  190. since the former is easier to type. However AppleScript does not like the former. You can get away with
  191.  
  192. Show variables {{x:x, y:y, z,z}}
  193.  
  194. The rest is copied from the last release of Show variables.
  195.  
  196.  
  197.  
  198.  
  199.